home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1995 April / Internet Tools.iso / osi / isode / dosisode / DOSISODE80.ZIP / ISODE8.WRK / UNIX / LIB / DNS / RES_SEND.C < prev    next >
Encoding:
C/C++ Source or Header  |  1991-12-16  |  10.2 KB  |  419 lines

  1. #include <fiddle.h>
  2. /*
  3.  * Copyright (c) 1985, 1989 Regents of the University of California.
  4.  * All rights reserved.
  5.  *
  6.  * Redistribution and use in source and binary forms are permitted
  7.  * provided that: (1) source distributions retain this entire copyright
  8.  * notice and comment, and (2) distributions including binaries display
  9.  * the following acknowledgement:  ``This product includes software
  10.  * developed by the University of California, Berkeley and its contributors''
  11.  * in the documentation or other materials provided with the distribution
  12.  * and in all advertising materials mentioning features or use of this
  13.  * software. Neither the name of the University nor the names of its
  14.  * contributors may be used to endorse or promote products derived
  15.  * from this software without specific prior written permission.
  16.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  17.  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  18.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  19.  */
  20.  
  21. #if defined(LIBC_SCCS) && !defined(lint)
  22. static char sccsid[] = "@(#)res_send.c    6.25 (Berkeley) 6/1/90";
  23. #endif /* LIBC_SCCS and not lint */
  24.  
  25. /*
  26.  * Send query to name server and wait for reply.
  27.  */
  28.  
  29. #include <sys/param.h>
  30. #include <sys/time.h>
  31. #include <sys/socket.h>
  32. #include <sys/uio.h>
  33. #include <netinet/in.h>
  34. #include <stdio.h>
  35. #include <errno.h>
  36. #include <arpa/nameser.h>
  37. #include <resolv.h>
  38.  
  39. extern int errno;
  40.  
  41. static int s = -1;    /* socket used for communications */
  42. static struct sockaddr no_addr;
  43.  
  44.  
  45. #ifndef FD_SET
  46. #define    NFDBITS        32
  47. #define    FD_SETSIZE    32
  48. #define    FD_SET(n, p)    ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
  49. #define    FD_CLR(n, p)    ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
  50. #define    FD_ISSET(n, p)    ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
  51. #define FD_ZERO(p)    bzero((char *)(p), sizeof(*(p)))
  52. #endif
  53.  
  54. res_send(buf, buflen, answer, anslen)
  55.     char *buf;
  56.     int buflen;
  57.     char *answer;
  58.     int anslen;
  59. {
  60.     register int n;
  61.     int try, v_circuit, resplen, ns;
  62.     int gotsomewhere = 0, connected = 0;
  63.     int connreset = 0;
  64.     u_short id, len;
  65.     char *cp;
  66.     fd_set dsmask;
  67.     struct timeval timeout;
  68.     HEADER *hp = (HEADER *) buf;
  69.     HEADER *anhp = (HEADER *) answer;
  70.     struct iovec iov[2];
  71.     int terrno = ETIMEDOUT;
  72.     char junk[512];
  73.  
  74. #ifdef DEBUG
  75.     if (_res.options & RES_DEBUG) {
  76.         printf("res_send()\n");
  77.         p_query(buf);
  78.     }
  79. #endif DEBUG
  80.     if (!(_res.options & RES_INIT))
  81.         if (res_init() == -1) {
  82.             return(-1);
  83.         }
  84.     v_circuit = (_res.options & RES_USEVC) || buflen > PACKETSZ;
  85.     id = hp->id;
  86.     /*
  87.      * Send request, RETRY times, or until successful
  88.      */
  89.     for (try = 0; try < _res.retry; try++) {
  90.        for (ns = 0; ns < _res.nscount; ns++) {
  91. #ifdef DEBUG
  92.         if (_res.options & RES_DEBUG)
  93.             printf("Querying server (# %d) address = %s\n", ns+1,
  94.                   inet_ntoa(_res.nsaddr_list[ns].sin_addr));
  95. #endif DEBUG
  96.     usevc:
  97.         if (v_circuit) {
  98.             int truncated = 0;
  99.  
  100.             /*
  101.              * Use virtual circuit;
  102.              * at most one attempt per server.
  103.              */
  104.             try = _res.retry;
  105.             if (s < 0) {
  106.                 s = socket(AF_INET, SOCK_STREAM, 0);
  107.                 if (s < 0) {
  108.                     terrno = errno;
  109. #ifdef DEBUG
  110.                     if (_res.options & RES_DEBUG)
  111.                         perror("socket (vc) failed");
  112. #endif DEBUG
  113.                     continue;
  114.                 }
  115.                 if (connect(s, &(_res.nsaddr_list[ns]),
  116.                    sizeof(struct sockaddr)) < 0) {
  117.                     terrno = errno;
  118. #ifdef DEBUG
  119.                     if (_res.options & RES_DEBUG)
  120.                         perror("connect failed");
  121. #endif DEBUG
  122.                     (void) close(s);
  123.                     s = -1;
  124.                     continue;
  125.                 }
  126.             }
  127.             /*
  128.              * Send length & message
  129.              */
  130.             len = htons((u_short)buflen);
  131.             iov[0].iov_base = (caddr_t)&len;
  132.             iov[0].iov_len = sizeof(len);
  133.             iov[1].iov_base = buf;
  134.             iov[1].iov_len = buflen;
  135.             if (writev(s, iov, 2) != sizeof(len) + buflen) {
  136.                 terrno = errno;
  137. #ifdef DEBUG
  138.                 if (_res.options & RES_DEBUG)
  139.                     perror("write failed");
  140. #endif DEBUG
  141.                 (void) close(s);
  142.                 s = -1;
  143.                 continue;
  144.             }
  145.             /*
  146.              * Receive length & response
  147.              */
  148.             cp = answer;
  149.             len = sizeof(short);
  150.             while (len != 0 &&
  151.                 (n = read(s, (char *)cp, (int)len)) > 0) {
  152.                 cp += n;
  153.                 len -= n;
  154.             }
  155.             if (n <= 0) {
  156.                 terrno = errno;
  157. #ifdef DEBUG
  158.                 if (_res.options & RES_DEBUG)
  159.                     perror("read failed");
  160. #endif DEBUG
  161.                 (void) close(s);
  162.                 s = -1;
  163.                 /*
  164.                  * A long running process might get its TCP
  165.                  * connection reset if the remote server was
  166.                  * restarted.  Requery the server instead of
  167.                  * trying a new one.  When there is only one
  168.                  * server, this means that a query might work
  169.                  * instead of failing.  We only allow one reset
  170.                  * per query to prevent looping.
  171.                  */
  172.                 if (terrno == ECONNRESET && !connreset) {
  173.                     connreset = 1;
  174.                     ns--;
  175.                 }
  176.                 continue;
  177.             }
  178.             cp = answer;
  179.             if ((resplen = ntohs(*(u_short *)cp)) > anslen) {
  180. #ifdef DEBUG
  181.                 if (_res.options & RES_DEBUG)
  182.                     fprintf(stderr, "response truncated\n");
  183. #endif DEBUG
  184.                 len = anslen;
  185.                 truncated = 1;
  186.             } else
  187.                 len = resplen;
  188.             while (len != 0 &&
  189.                (n = read(s, (char *)cp, (int)len)) > 0) {
  190.                 cp += n;
  191.                 len -= n;
  192.             }
  193.             if (n <= 0) {
  194.                 terrno = errno;
  195. #ifdef DEBUG
  196.                 if (_res.options & RES_DEBUG)
  197.                     perror("read failed");
  198. #endif DEBUG
  199.                 (void) close(s);
  200.                 s = -1;
  201.                 continue;
  202.             }
  203.             if (truncated) {
  204.                 /*
  205.                  * Flush rest of answer
  206.                  * so connection stays in synch.
  207.                  */
  208.                 anhp->tc = 1;
  209.                 len = resplen - anslen;
  210.                 while (len != 0) {
  211.                     n = (len > sizeof(junk) ?
  212.                         sizeof(junk) : len);
  213.                     if ((n = read(s, junk, n)) > 0)
  214.                         len -= n;
  215.                     else
  216.                         break;
  217.                 }
  218.             }
  219.         } else {
  220.             /*
  221.              * Use datagrams.
  222.              */
  223.             if (s < 0) {
  224.                 s = socket(AF_INET, SOCK_DGRAM, 0);
  225.                 if (s < 0) {
  226.                     terrno = errno;
  227. #ifdef DEBUG
  228.                     if (_res.options & RES_DEBUG)
  229.                         perror("socket (dg) failed");
  230. #endif DEBUG
  231.                     continue;
  232.                 }
  233.             }
  234. #if    BSD >= 43
  235.             /*
  236.              * I'm tired of answering this question, so:
  237.              * On a 4.3BSD+ machine (client and server,
  238.              * actually), sending to a nameserver datagram
  239.              * port with no nameserver will cause an
  240.              * ICMP port unreachable message to be returned.
  241.              * If our datagram socket is "connected" to the
  242.              * server, we get an ECONNREFUSED error on the next
  243.              * socket operation, and select returns if the
  244.              * error message is received.  We can thus detect
  245.              * the absence of a nameserver without timing out.
  246.              * If we have sent queries to at least two servers,
  247.              * however, we don't want to remain connected,
  248.              * as we wish to receive answers from the first
  249.              * server to respond.
  250.              */
  251.             if (_res.nscount == 1 || (try == 0 && ns == 0)) {
  252.                 /*
  253.                  * Don't use connect if we might
  254.                  * still receive a response
  255.                  * from another server.
  256.                  */
  257.                 if (connected == 0) {
  258.                     if (connect(s, &_res.nsaddr_list[ns],
  259.                         sizeof(struct sockaddr)) < 0) {
  260. #ifdef DEBUG
  261.                         if (_res.options & RES_DEBUG)
  262.                             perror("connect");
  263. #endif DEBUG
  264.                         continue;
  265.                     }
  266.                     connected = 1;
  267.                 }
  268.                 if (send(s, buf, buflen, 0) != buflen) {
  269. #ifdef DEBUG
  270.                     if (_res.options & RES_DEBUG)
  271.                         perror("send");
  272. #endif DEBUG
  273.                     continue;
  274.                 }
  275.             } else {
  276.                 /*
  277.                  * Disconnect if we want to listen
  278.                  * for responses from more than one server.
  279.                  */
  280.                 if (connected) {
  281.                     (void) connect(s, &no_addr,
  282.                         sizeof(no_addr));
  283.                     connected = 0;
  284.                 }
  285. #endif BSD
  286.                 if (sendto(s, buf, buflen, 0,
  287.                     &_res.nsaddr_list[ns],
  288.                     sizeof(struct sockaddr)) != buflen) {
  289. #ifdef DEBUG
  290.                     if (_res.options & RES_DEBUG)
  291.                         perror("sendto");
  292. #endif DEBUG
  293.                     continue;
  294.                 }
  295. #if    BSD >= 43
  296.             }
  297. #endif
  298.  
  299.             /*
  300.              * Wait for reply
  301.              */
  302.             timeout.tv_sec = (_res.retrans << try);
  303.             if (try > 0)
  304.                 timeout.tv_sec /= _res.nscount;
  305.             if (timeout.tv_sec <= 0)
  306.                 timeout.tv_sec = 1;
  307.             timeout.tv_usec = 0;
  308. wait:
  309.             FD_ZERO(&dsmask);
  310.             FD_SET(s, &dsmask);
  311.             n = select(s+1, &dsmask, (fd_set *)NULL,
  312.                 (fd_set *)NULL, &timeout);
  313.             if (n < 0) {
  314. #ifdef DEBUG
  315.                 if (_res.options & RES_DEBUG)
  316.                     perror("select");
  317. #endif DEBUG
  318.                 continue;
  319.             }
  320.             if (n == 0) {
  321.                 /*
  322.                  * timeout
  323.                  */
  324. #ifdef DEBUG
  325.                 if (_res.options & RES_DEBUG)
  326.                     printf("timeout\n");
  327. #endif DEBUG
  328. #if BSD >= 43
  329.                 gotsomewhere = 1;
  330. #endif
  331.                 continue;
  332.             }
  333.             if ((resplen = recv(s, answer, anslen, 0)) <= 0) {
  334. #ifdef DEBUG
  335.                 if (_res.options & RES_DEBUG)
  336.                     perror("recvfrom");
  337. #endif DEBUG
  338.                 continue;
  339.             }
  340.             gotsomewhere = 1;
  341.             if (id != anhp->id) {
  342.                 /*
  343.                  * response from old query, ignore it
  344.                  */
  345. #ifdef DEBUG
  346.                 if (_res.options & RES_DEBUG) {
  347.                     printf("old answer:\n");
  348.                     p_query(answer);
  349.                 }
  350. #endif DEBUG
  351.                 goto wait;
  352.             }
  353.             if (!(_res.options & RES_IGNTC) && anhp->tc) {
  354.                 /*
  355.                  * get rest of answer;
  356.                  * use TCP with same server.
  357.                  */
  358. #ifdef DEBUG
  359.                 if (_res.options & RES_DEBUG)
  360.                     printf("truncated answer\n");
  361. #endif DEBUG
  362.                 (void) close(s);
  363.                 s = -1;
  364.                 v_circuit = 1;
  365.                 goto usevc;
  366.             }
  367.         }
  368. #ifdef DEBUG
  369.         if (_res.options & RES_DEBUG) {
  370.             printf("got answer:\n");
  371.             p_query(answer);
  372.         }
  373. #endif DEBUG
  374.         /*
  375.          * If using virtual circuits, we assume that the first server
  376.          * is preferred * over the rest (i.e. it is on the local
  377.          * machine) and only keep that one open.
  378.          * If we have temporarily opened a virtual circuit,
  379.          * or if we haven't been asked to keep a socket open,
  380.          * close the socket.
  381.          */
  382.         if ((v_circuit &&
  383.             ((_res.options & RES_USEVC) == 0 || ns != 0)) ||
  384.             (_res.options & RES_STAYOPEN) == 0) {
  385.             (void) close(s);
  386.             s = -1;
  387.         }
  388.         return (resplen);
  389.        }
  390.     }
  391.     if (s >= 0) {
  392.         (void) close(s);
  393.         s = -1;
  394.     }
  395.     if (v_circuit == 0)
  396.         if (gotsomewhere == 0)
  397.             errno = ECONNREFUSED;    /* no nameservers found */
  398.         else
  399.             errno = ETIMEDOUT;    /* no answer obtained */
  400.     else
  401.         errno = terrno;
  402.     return (-1);
  403. }
  404.  
  405. /*
  406.  * This routine is for closing the socket if a virtual circuit is used and
  407.  * the program wants to close it.  This provides support for endhostent()
  408.  * which expects to close the socket.
  409.  *
  410.  * This routine is not expected to be user visible.
  411.  */
  412. _res_close()
  413. {
  414.     if (s != -1) {
  415.         (void) close(s);
  416.         s = -1;
  417.     }
  418. }
  419.